package com.ruoyi.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.config.EncryptConfig;
import com.ruoyi.common.config.PayBaseInfo;
import com.ruoyi.common.config.RedissonLockComponent;
import com.ruoyi.common.enums.BusinessEnum;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.common.vo.PayReq;
import com.ruoyi.common.vo.PayRes;
import com.ruoyi.system.assembler.PayAssembler;
import com.ruoyi.system.domain.PaymentRecord;
import com.ruoyi.system.domain.Product;
import com.ruoyi.system.domain.RechargeRecord;
import com.ruoyi.system.domain.vo.ClientInfoVo;
import com.ruoyi.system.domain.vo.NotifyResult;
import com.ruoyi.system.domain.vo.PaySubmitOutVo;
import com.ruoyi.system.domain.vo.PaySubmitVo;
import com.ruoyi.system.service.IPayService;
import com.ruoyi.system.service.IPaymentRecordService;
import com.ruoyi.system.service.IProductService;
import com.ruoyi.system.service.IRechargeRecordService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class PayServiceImpl implements IPayService {

    @Autowired
    private IProductService productService;

    @Autowired
    private IPaymentRecordService paymentRecordService;

    @Autowired
    private IRechargeRecordService rechargeRecordService;

    @Autowired
    private PayBaseInfo payBaseInfo;

    @Autowired
    private RedissonLockComponent redissonLockComponent;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private EncryptConfig encryptConfig;


    @Override
    @Transactional
    public PaySubmitOutVo paySubmit(PaySubmitVo paySubmitVo, HttpServletRequest request) throws Exception {
        if (StrUtil.isBlank(paySubmitVo.getBuyer())) {
            throw new ServiceException("请输入邮箱");
        }
        // 判断验证码是否相等
        if (!StrUtil.equalsIgnoreCase(paySubmitVo.getVerifyCode(), redisTemplate.opsForValue().get("send_verify_code:" + paySubmitVo.getBuyer()))) {
            throw new ServiceException("验证码错误，请重新提交");
        }
        // 判断商品类型 如果是需要重置的业务重写计算金额

        PaymentRecord paymentRecord = paymentRecordService.createRecordByPaySubmit(paySubmitVo, request);

        PayBaseInfo info = payBaseInfo.getPayBaseInfo();
        PayReq payReq = PayAssembler.INSTANCE.convertPaymentRecordToPayReq(paymentRecord, info);
        String sign = this.buildSign(payReq);
        if (StrUtil.isBlank(sign)) {
            throw new ServiceException("生成签名失败");
        }
        payReq.setSign(sign);
        // 发起支付请求
        PayRes payRes = null;
        try {
            String body = HttpUtil.post(info.getPay_require_url(), BeanUtil.beanToMap(payReq));
            payRes = JSON.toJavaObject(JSONObject.parseObject(body), PayRes.class);
        } catch (Exception e) {
            throw new ServiceException("发起支付请求失败：" + e.getMessage());
        }
        if (Objects.isNull(payRes)){
            throw new ServiceException("获取支付请求结果失败");
        }
        if (NumberUtil.compare(payRes.getCode(), 1) != 0){
            log.error(payRes.getMsg());
            throw new ServiceException("获取支付请求结果失败");
        }

        // 删除验证码
        redisTemplate.delete("send_verify_code:" + paySubmitVo.getBuyer());
        // 保存
        PaySubmitOutVo paySubmitOutVo = new PaySubmitOutVo();
        paySubmitOutVo.setUrl(payRes.getPayurl());
        // 设置客户端token  1天过期
        String clientToken = encryptConfig.symmetricEncryt(paySubmitVo.getBuyer());
        // 保存到redis中
        redisTemplate.opsForValue().set(clientToken, paySubmitVo.getBuyer(), 1, TimeUnit.DAYS );
        paySubmitOutVo.setClientToken(clientToken);
        return paySubmitOutVo;
    }

    @Override
    public String buildSign(Object object) {
        PayBaseInfo info = payBaseInfo.getPayBaseInfo();
        HashMap<String, Object> hashMap = BeanUtil.copyProperties(object, HashMap.class,"sign_type", "sign", "serialVersionUID");
        String join = MapUtil.join(MapUtil.sort(MapUtil.removeNullValue(hashMap)), "&", "=") +  info.getKey();
        String sign = null;
        if ("MD5".equals(info.getSign_type())) {
            sign = DigestUtil.md5Hex(join);
        }
        return sign;
    }

    @Override
    @Transactional
    public String payNotify(NotifyResult notifyResult) {
        if (Objects.isNull(notifyResult.getTrade_no())) {
            return null;
        }
        if (!"TRADE_SUCCESS".equals(notifyResult.getTrade_status())) {
            log.info("订单：【{}】，支付失败", notifyResult.getOut_trade_no());
            return null;
        }

        // 使用分布式锁
        Boolean result = redissonLockComponent.execByDistributedLock("update_payment_record:" + notifyResult.getTrade_no(), 2L, () -> {
            PaymentRecord paymentRecord = paymentRecordService.getById(notifyResult.getOut_trade_no());
            if (Objects.isNull(paymentRecord)) {
                log.warn("支付记录不存在，支付回调信息：【{}】", JSONObject.toJSONString(notifyResult));
                return false;
            }

            String sign = buildSign(notifyResult);
            if (!StrUtil.equals(sign, notifyResult.getSign())) {
                log.warn("付款记录【{}】签名校验失败，付款回调签名【{}】", sign, notifyResult.getSign());
                return false;
            }
            if (StrUtil.equalsAny(paymentRecord.getPayStatus(), "2", "3")) {
                return true;
            }

            Product product = productService.getById(paymentRecord.getProductId());

            // 查看库存是否足够
            String msg = null;
            if ("1".equals(paymentRecord.getPayStatus()) && StrUtil.isBlank(paymentRecord.getTradeNo())) {
                LambdaUpdateWrapper<PaymentRecord> updateWrapper = Wrappers.lambdaUpdate(PaymentRecord.class);
                updateWrapper.eq(PaymentRecord::getId, notifyResult.getOut_trade_no())
                        .set(PaymentRecord::getTradeNo, notifyResult.getTrade_no());

                if (BusinessEnum.ProductType.SELLE.getCode().equals(product.getType())) {
                     // 库存扣减失败也需要保存
                    Object savePoint = null;
                    try {
                        //设置回滚点
                        savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
                        productService.reduceStock(paymentRecord.getProductId(), paymentRecord.getCount(), paymentRecord.getId());
                        updateWrapper.set(PaymentRecord::getPayStatus, "2");
                    } catch (Exception e) {
                        updateWrapper.set(PaymentRecord::getPayStatus, "3");
                        msg = "库存扣减失败";
                        //手工回滚异常
                        TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);
                    }

                } else if (BusinessEnum.ProductType.RECHARGE.getCode().equals(product.getType())) {
                    // 充值业务，只需将状态修改为已支付待发货，等待供应商
                    updateWrapper.set(PaymentRecord::getPayStatus, "3");
                    msg = "待充值";
                }

                updateWrapper.set(PaymentRecord::getMsg, msg);
                // 修改支付记录
                paymentRecordService.update(updateWrapper);
            }
            notifyResult.getTrade_no();
            return true;
        });

        return result ? "success" : null;
    }

    public static void main(String[] args) {
//        NotifyResult result = new NotifyResult();
//        result.setPid(2446);
//        result.setTrade_no("2023070521303424200");
//        result.setOut_trade_no("1676584350570811394");
//        result.setType("wxpay");
//        result.setMoney("0.02");
//        result.setTrade_status("TRADE_SUCCESS");
//        result.setSign("47b89e923320c87b24e514b2d86b0341");
//        result.setSign_type("MD5");
//
//
//        HashMap hashMap = BeanUtil.copyProperties(result, HashMap.class, "sign_type", "sign", "serialVersionUID");
//        String join = MapUtil.join(MapUtil.sort(MapUtil.removeNullValue(hashMap)), "&", "=");
//        System.out.println("hashMap = " + hashMap);

        System.out.println(StrUtil.equalsIgnoreCase("mkyi", "MkYi"));
        System.out.println(StrUtil.equalsIgnoreCase("mkyi", "mkyl"));

    }
}
